import { V3proAlertMessageFile } from "../model/v3pro.model";
import { Canvas, CanvasRenderingContext2D } from "canvas";
import { props } from "ramda";
import { FileResult } from "tmp";
import fs from "node:fs";
import { ImageLayout } from "../model/v3pro.image.layout";
import { V3proScreenLayoutHelper } from "./v3pro.screen.layout.helper";
import { computeHash, createTmpFile, getSuffix } from "../../../utils/file.util";
import { SRFileTypeEnum } from "../../starriver-task/model/sr.enum";
import { V3proLayout } from "../../../api/v3pro-api/v3pro.api.response.model";
import { randomUUID } from "node:crypto";
import { PanelLayout, ScanBoardLayout } from "../model/v3pro.image.layout.extend";

/**
 * 屏幕图片绘制
 */
export class V3proScreenImageHelper {
  constructor(
    private __helper: V3proScreenLayoutHelper,
    private __levels: Array<"sendBox" | "outPort" | "scanBoard" | "panel" | "pixel"> = [
      "sendBox",
      "outPort",
      "scanBoard",
      "panel",
    ],
  ) {
    this.__screen = this.__helper.screenLayout;
    this.__canvas = new Canvas(this.__screen.width, this.__screen.height, "image"); //createCanvas();
    this.__ctx = this.__canvas.getContext("2d");
    this.__ctx.globalAlpha = 1;
    this.__ctx.imageSmoothingEnabled = false;
  }

  private __screen: ImageLayout;
  private __canvas: Canvas;
  private __ctx: CanvasRenderingContext2D;

  /**
   * 获取扫描版文本内容
   * @param scanBoards
   */
  private __scanBoardsTextGenerator = (scanBoards: ScanBoardLayout) => {
    return scanBoards.addr.split(".").slice(2).join(".");
  };

  setScanBoardsTestGenerator(generator: (scanBoards: ScanBoardLayout) => string) {
    this.__scanBoardsTextGenerator = generator;
    return this;
  }

  /**
   * 颜色
   * @private
   */
  private __colors = {
    gray: "rgb(217, 217, 217)",
    gray2: "rgb(200, 200, 200)",
    red: "rgb(143, 8, 15)",
    green: "rgb(17, 116, 0)",
    white: "rgb(255, 255, 255)",
    black: "rgb(0, 0, 0)",
  };

  drawBackground() {
    // if (this.__levels.includes("panel")) {
    //   this.__ctx.fillStyle = this.__colors.green;
    // } else {
    //   this.__ctx.fillStyle = this.__colors.green;
    // }
    this.__ctx.fillStyle = this.__colors.black;
    const [width, height] = props(["width", "height"], this.__screen as V3proLayout);
    this.__ctx.fillRect(0, 0, width, height);
    return this;
  }

  /**
   * 绘制边框
   * 使用带透明度的线条绘制边框（为了避免把坏点遮住）；
   *
   * 1. 扫描版边框
   * 2. 面板边框
   */
  drawBorders() {
    this.__ctx.lineWidth = 1;
    this.__ctx.strokeStyle = "rgba(185,185,185,0.5)";
    for (const p of this.__helper.panelLayouts) {
      this.__ctx.strokeRect(p.x, p.y, p.width, p.height);
    }
    this.__ctx.strokeStyle = "rgba(137,168,92,0.5)";
    for (const scanBoard of this.__helper.scanBoardLayouts) {
      this.__ctx.strokeRect(scanBoard.x, scanBoard.y, scanBoard.width, scanBoard.height);
    }
    return this;
  }

  private __colorCss = {
    scanBoard: {
      txtNormalFillColor: this.__colors.green,
      txtErrorFillColor: this.__colors.red,
      txtNormalFontColor: this.__colors.white,
      txtErrorFontColor: this.__colors.white,
      scanBoardNormalBorderColor: this.__colors.green,
      scanBoardErrorBorderColor: this.__colors.red,
      panelNormalFillColor: this.__colors.gray,
      panelErrorFillColor: this.__colors.gray,
      pixelErrorFillColor: this.__colors.gray,
    },
    panel: {
      txtNormalFillColor: this.__colors.gray,
      txtErrorFillColor: this.__colors.gray,
      txtNormalFontColor: this.__colors.black,
      txtErrorFontColor: this.__colors.black,
      scanBoardNormalBorderColor: this.__colors.gray,
      scanBoardErrorBorderColor: this.__colors.gray,
      panelNormalFillColor: this.__colors.green,
      panelErrorFillColor: this.__colors.red,
      pixelErrorFillColor: this.__colors.black,
    },
  };

  /**
   * 绘制
   * @param isDeadPixel
   * @param drawPixel
   */
  draw(isDeadPixel = false, drawPixel = false) {
    this.drawBackground();
    const [isScanBoard, isPanel] = [
      this.__levels.includes("scanBoard"),
      this.__levels.includes("panel"),
    ];
    const [scanBoardCss, panelCss] = [
      isScanBoard ? this.__colorCss.scanBoard : this.__colorCss.panel,
      isPanel ? this.__colorCss.panel : this.__colorCss.scanBoard,
    ];
    const helper = this.__helper;
    helper.panelLayouts.forEach((panel) => {
      this.__drawFillRect(
        panel,
        panel.isError ? panelCss.panelErrorFillColor : panelCss.panelNormalFillColor,
      );
      if (isDeadPixel) {
        if (drawPixel) {
          let start: [number, number] = [-1, -1];
          let end: [number, number] = [-1, -1];
          panel.pixels.forEachTrue((x, y) => {
            if (end[1] !== y || end[0] + 1 !== x) {
              this.__drawPixel(...start, ...end, panelCss.pixelErrorFillColor);
              start = [x, y];
              end = [x, y];
              return;
            }
            end = [x, y];
          });
        } else if (panel.isDeadPixelErr()) {
          this.__drawDeadPixelRate(panel);
        }
      }
      this.__drawRect(panel, this.__colors.white);
    });

    helper.scanBoardLayouts.forEach((scanBoard) => {
      this.__drawRect(
        scanBoard,
        scanBoard.isError
          ? scanBoardCss.scanBoardErrorBorderColor
          : isScanBoard && isPanel
            ? this.__colorCss.panel.scanBoardNormalBorderColor
            : scanBoardCss.scanBoardNormalBorderColor,
        6,
      );
      this.__drawScanBoardText(
        scanBoard,
        scanBoard.isError ? scanBoardCss.txtErrorFillColor : scanBoardCss.txtNormalFillColor,
        scanBoard.isError ? scanBoardCss.txtErrorFontColor : scanBoardCss.txtNormalFontColor,
      );
    });
    return this;
  }

  private __drawDeadPixelRate(panel: PanelLayout) {
    const height = panel.width / 5;
    const text = `${Math.round(panel.getErrPixelRate() * 100)}%`;
    this.__ctx.textAlign = "center";
    this.__ctx.font = `${height}px sans-serif`;
    this.__ctx.fillStyle = this.__colors.white;
    this.__ctx.fillText(text, panel.x + panel.width / 2, panel.y + height + 10);
    this.__ctx.textAlign = "left";
  }

  private __drawScanBoardText(
    scanBoard: ScanBoardLayout,
    fillColor: string,
    textColor: string = "white",
  ) {
    let height = scanBoard.width / 10;
    if (scanBoard.panels.length()) {
      height = scanBoard.panels.getByIndex(0).height / 3;
    }
    const fontSize = height / 1.5;
    const text = this.__scanBoardsTextGenerator(scanBoard);
    this.__ctx.font = `${fontSize}px sans-serif`;
    const txtWidth = this.__ctx.measureText(text).width;
    const width = txtWidth + fontSize * 2;
    const [recX, recY, recWidth, recHeight] = [
      (scanBoard.width - width) / 2 + scanBoard.x,
      scanBoard.y + scanBoard.height - height - 10,
      width,
      height,
    ];
    this.__drawFillRect(
      new ImageLayout({
        x: recX,
        y: recY,
        width: recWidth,
        height: recHeight,
      }),
      fillColor,
    );
    this.__drawRect(
      new ImageLayout({
        x: recX,
        y: recY,
        width: recWidth,
        height: recHeight,
      }),
      this.__colors.gray2,
    );

    this.__ctx.fillStyle = textColor;
    this.__ctx.fillText(
      text,
      (scanBoard.width - txtWidth) / 2 + scanBoard.x,
      scanBoard.y + scanBoard.height - height - 10 + fontSize + (height - fontSize) / 2,
    );
    return this;
  }

  /**
   * 绘制线
   * @param x1
   * @param y1
   * @param x2
   * @param y2
   * @param color
   * @param lineWidth
   * @private
   */
  private __drawLine(x1: number, y1: number, x2: number, y2: number, color: string, lineWidth = 2) {
    if (x1 < 0) return this;
    this.__ctx.lineWidth = lineWidth;
    this.__ctx.strokeStyle = color;
    this.__ctx.beginPath();
    this.__ctx.moveTo(x1, y1);
    this.__ctx.lineTo(x2, y2);
    this.__ctx.stroke();
    return this;
  }

  /**
   * 绘制矩形
   * @param layout
   * @param color
   * @param lineWidth
   * @private
   */
  private __drawRect(layout: ImageLayout, color: string, lineWidth = 2) {
    this.__ctx.lineWidth = lineWidth;
    this.__ctx.strokeStyle = color;
    const outerBorder = lineWidth / 2;
    this.__ctx.strokeRect(
      layout.x + outerBorder,
      layout.y + outerBorder,
      layout.width - lineWidth,
      layout.height - lineWidth,
    );
    return this;
  }

  /**
   * 绘制填充矩形
   * @param layout
   * @param color
   * @param lineWidth
   * @private
   */
  private __drawFillRect(layout: ImageLayout, color: string, lineWidth = 0) {
    this.__ctx.lineWidth = lineWidth;
    this.__ctx.fillStyle = color;
    this.__ctx.strokeStyle = color;
    this.__ctx.fillRect(layout.x, layout.y, layout.width, layout.height);
    return this;
  }

  /**
   * 绘制像素点
   * @param x
   * @param y
   * @param x2
   * @param y2
   * @param color
   * @private
   */
  private __drawPixel(x: number, y: number, x2: number, y2: number, color: string) {
    if (x < 0) return;
    this.__ctx.fillStyle = color;
    this.__ctx.lineWidth = 0;
    this.__ctx.fillRect(x, y, x2 - x + 1, y2 - y + 1);
  }

  async export2TmpResult(file: FileResult) {
    return await this.export2File(file.name);
  }

  async export2AlertMessageFile(type: SRFileTypeEnum = SRFileTypeEnum.SCREEN_IMAGE) {
    const file = createTmpFile(undefined, "png");
    await this.export2TmpResult(file);
    return new V3proAlertMessageFile(
      type,
      file,
      await computeHash(file.name),
      [randomUUID(), "png"].join("."),
    );
  }

  private __getReadableByFilePath(filePath: string) {
    const suffix = getSuffix(filePath) || "";
    switch (suffix) {
      case "png":
        return this.__canvas.createPNGStream();
      case "jpeg":
      case "jpg":
        return this.__canvas.createJPEGStream();
      case "pdf":
        return this.__canvas.createPDFStream();
      default:
        throw new Error("不支持的图片格式");
    }
  }

  async export2File(filePath: string) {
    return new Promise((resolve, reject) => {
      try {
        const out = fs.createWriteStream(filePath);
        const stream = this.__getReadableByFilePath(filePath);
        stream.pipe(out);
        out.on("finish", () => {
          out.end();
          resolve(filePath);
        });
      } catch (e) {
        reject(e);
      }
    });
  }

  destroy() {
    this.__helper = undefined!;
    this.__canvas = undefined!;
    this.__ctx = undefined!;
    this.__screen = undefined!;
  }
}
